package ar.edu.unq.wannaplay.services;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.joda.time.Interval;
import org.springframework.transaction.annotation.Transactional;

import ar.edu.unq.wannaplay.calendareable.Licence;
import ar.edu.unq.wannaplay.dtos.EmployeeDTO;
import ar.edu.unq.wannaplay.dtos.EmployeeLicenceDTO;
import ar.edu.unq.wannaplay.dtos.LicenceDTO;
import ar.edu.unq.wannaplay.dtos.SkillDTO;
import ar.edu.unq.wannaplay.dtos.TypeOfLicenceDTO;
import ar.edu.unq.wannaplay.employee.Employee;
import ar.edu.unq.wannaplay.employee.PersonalData;
import ar.edu.unq.wannaplay.employee.Register;
import ar.edu.unq.wannaplay.employee.Skill;
import ar.edu.unq.wannaplay.exceptions.EmailSyntaxException;
import ar.edu.unq.wannaplay.exceptions.EmployeeCantTakeLicenceException;
import ar.edu.unq.wannaplay.exceptions.NullArgumentException;
import ar.edu.unq.wannaplay.persistence.daos.EmployeeDAO;
import ar.edu.unq.wannaplay.utils.DTOHelper;
import ar.edu.unq.wannaplay.utils.EmailValidator;
import ar.edu.unq.wannaplay.utils.LicenceCalendar;
import ar.edu.unq.wannaplay.utils.Service;

public class EmployeeService implements Service<Employee, EmployeeDTO>, ServiceDeleteDTO<EmployeeDTO> {
    private static final long serialVersionUID = 1L;

    private LicenceService licenceService;

    private EmailValidator emailValidator;

    private EmployeeDAO dao;

    public EmployeeDAO getDao() {
        return dao;
    }

    public void setDao(final EmployeeDAO dao) {
        this.dao = dao;
    }

    private LicenceCalendar getHolidaysCalendar(final int month, final int year) {
        return this.getDao().getHolidaysCalendar(month, year);
    }

    private void save(final Employee entity) {
        this.getDao().save(entity);
    }

    private void delete(final Employee entity) {
        this.getDao().delete(entity);
    }

    private void update(final Employee entity) {
        this.getDao().update(entity);
    }

    private Employee findById(final Serializable id) {
        return this.getDao().findById(id);
    }

    private List<Employee> findAll() {
        return this.getDao().findAll();
    }

    @Transactional
    public void giveLicenceTo(final String dni, final TypeOfLicenceDTO typeDTO, final Interval interval)
            throws EmployeeCantTakeLicenceException, NullArgumentException {
        if (typeDTO == null) {
            throw new NullArgumentException("type can't be null");
        }
        Employee emp = this.getDao().findEmployeeByDNI(dni);
        this.getLicenceService().giveLicenceToEmployee(typeDTO.getName(), emp, interval);
    }

    // XXX deprecated
    @Transactional
    public void saveEmployee(final EmployeeDTO entity) {
        this.save(this.fromDto(entity));
    }

    @Transactional
    public void saveEmployee(final PersonalData data, final String art, final List<SkillDTO> skillsDTO)
            throws EmailSyntaxException {
        String trimEmail = data.getEmail().trim();
        if (!emailValidator.validate(trimEmail)) {
            throw new EmailSyntaxException("Email not written properly");
        }
        List<Skill> skills = new ArrayList<Skill>();
        for (SkillDTO sDTO : skillsDTO) {
            skills.add(sDTO.fromDto());
        }

        Employee employee = new Employee(new Register(), data, null, art, null, skills);
        this.save(employee);
    }

    @Transactional
    public List<EmployeeDTO> findAllDTOs() {
        return this.toDTOs(this.findAll());
    }

    @Transactional
    public void deleteById(final int id) {
        this.getDao().deleteById(id);
    }

    private List<EmployeeDTO> toDTOs(final List<Employee> employees) {
        DTOHelper<Employee, EmployeeDTO> helper = new DTOHelper<Employee, EmployeeDTO>();
        return helper.toDtos(employees, this);
    }

    @Transactional
    public Employee getEmployeeById(final String id) {
        return this.findById(Integer.parseInt(id));
    }

    private Employee fromDto(final EmployeeDTO entity) {
        return entity.fromDto();
    }

    @Override
    @Transactional
    public void delete(final EmployeeDTO employee) {
        this.delete(employee.fromDto());
    }

    @Transactional
    public void updateEmployee(final PersonalData data, final String art, final List<SkillDTO> skillsDTO)
            throws EmailSyntaxException {
        String trimEmail = data.getEmail().trim();
        if (!emailValidator.validate(trimEmail)) {
            throw new EmailSyntaxException("Email not written properly");
        }
        Employee employee = this.getDao().findEmployeeByDNI(data.getId());
        employee.getData().setAddress(data.getAddress());
        employee.getData().setEmail(data.getEmail());
        employee.getData().setName(data.getName());
        employee.getData().setSurname(data.getSurname());
        employee.getData().setTelephone(data.getTelephone());
        employee.getData().setUniversity(data.getUniversity());
        employee.getData().setTown(data.getTown());

        employee.setArt(art);

        List<Skill> skills = new ArrayList<Skill>();
        for (SkillDTO sDTO : skillsDTO) {
            skills.add(sDTO.fromDto());
        }

        employee.setSkills(skills);

        this.update(employee);
    }

    @Transactional
    public void updateEmployee(final String id, final List<SkillDTO> skillsDTO) {
        Employee employee = this.getDao().findEmployeeByDNI(id);
        List<Skill> skills = new ArrayList<Skill>();
        for (SkillDTO sDTO : skillsDTO) {
            skills.add(sDTO.fromDto());
        }

        employee.setSkills(skills);

        this.update(employee);
    }

    @Transactional
    public List<LicenceDTO> getLicencesDTO(final String dni) {
        Employee emp = this.getDao().findEmployeeByDNI(dni);
        List<Licence> licences = emp.getRegister().getLicences();
        List<LicenceDTO> licencesDTO = new ArrayList<LicenceDTO>();

        for (Licence l : licences) {
            licencesDTO.add(new LicenceDTO(l));
        }

        return licencesDTO;
    }

    @Override
    public EmployeeDTO toDto(final Employee object) {
        return new EmployeeDTO(object.getData(), object.getSkills(), object.getRegister().getLicences());
    }

    @Transactional
    public EmployeeDTO findEmployeeDTOByDNI(final String dni) {
        return this.toDto(this.getDao().findEmployeeByDNI(dni));
    }

    public LicenceService getLicenceService() {
        return licenceService;
    }

    public void setLicenceService(final LicenceService licenceService) {
        this.licenceService = licenceService;
    }

    public List<EmployeeLicenceDTO> findAllEmployeeLicenceDTO(final int month, final int year) {
        LicenceCalendar calendar = this.getHolidaysCalendar(month, year);
        List<EmployeeLicenceDTO> employeeLicencesDTOs = new ArrayList<EmployeeLicenceDTO>();

        Map<Integer, String> dayLicencesMap;
        for (String name : calendar.getEmployeesNames()) {
            dayLicencesMap = calendar.getDayLicencesMap(name);
            employeeLicencesDTOs.add(new EmployeeLicenceDTO(name, dayLicencesMap));
        }

        return employeeLicencesDTOs;
    }

    public EmailValidator getEmailValidator() {
        return emailValidator;
    }

    public void setEmailValidator(final EmailValidator emailValidator) {
        this.emailValidator = emailValidator;
    }
}
